/*******************************************************************************
 * Copyright 2011 Alexandre Zglav and Morphiastic
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.morphiastic;

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.IDN;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bson.BSONObject;
import org.bson.types.ObjectId;
import org.elasticsearch.action.admin.indices.settings.UpdateSettingsRequest;
import org.elasticsearch.bootstrap.Bootstrap;
import org.elasticsearch.client.Client;
//import org.elasticsearch.client.action.admin.indices.create.CreateIndexRequestBuilder;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.code.morphia.annotations.Id;
import com.google.code.morphia.annotations.Transient;
import com.google.code.morphia.logging.Logr;
import com.google.code.morphia.logging.MorphiaLoggerFactory;
import com.google.code.morphia.utils.ReflectionUtils;
import com.google.common.collect.Multiset.Entry;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.util.Hash;
 
import com.morphiastic.analysis.filters.*;
import com.morphiastic.analysis.analyzers.*;
import com.morphiastic.analysis.tokenizers.*;
import com.morphiastic.annotations.*;

import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;


import java.lang.reflect.AccessibleObject;

/**
 * Mapping definitions of a class that should be indexed 
 * 
 * Represents a Type in ElasticSearch 
 * 
 * 
 * @author Alex
 *
 */
public class IndexedClass {

	public static List<Class<? extends Annotation>> fieldAnnotationsLookup = new ArrayList<Class<? extends Annotation>>(Arrays.asList(	
			ElastiField.class
	));
		
	public static List<Class<? extends Annotation>> classAnnotationsLookup = new ArrayList<Class<? extends Annotation>>(Arrays.asList(
			ElastiMorphed.class
	));
	
	public static final String ENABLED_PROPKEY = "enabled";
	public static final String STORE_KEYPROP = "store";
	public static final String TERMVECTOR_PROPKEY = "term_vector";
	public static final String ANALYZER_PROPKEY = "analyzer";
	public static final String SEARCH_ANALYZER_PROPKEY = "search_analyzer";
	public static final String INDEX_PROPKEY = "index";
	public static final String PATH_PROPKEY = "path";
	public static final String NAME_PROPKEY = "name";
	public static final String NULL_VALUE_PROPKEY = "null_value";
	public static final String STORE_PROPKEY = "store";
	public static final String TYPE_PROPKEY = "type";
	public static final String REQUIRED_PROPKEY = "required";
	public static final String COMPRESS_PROPKEY = "compress";
	public static final String COMPRESS_THRESHOLD_PROPKEY = "compress_threshold";
	public static final String INCLUDES_PROPKEY = "includes";
	public static final String EXCLUDES_PROPKEY = "excludes";
	public static final String FORMAT_PROPKEY = "format";
	public static final String DEFAULT_PROPKEY = "default";
	
	
	public static final String ELASTICSEARCH_DOCUMENT_ID_FIELD_KEY = "_id";
	
	public static final String MID_FIELD_KEY = "_mid";
	public static final ElastiMorphed defaultElastimorphedAnnotation = DefaultElastiMorphedValueAccessor.get();
	public static final _analyzer default_analyzer = defaultElastimorphedAnnotation._analyzer();
	public static final _all default_all = defaultElastimorphedAnnotation._all();
	public static final _boost default_boost = defaultElastimorphedAnnotation._boost();
	public static final _id default_id = defaultElastimorphedAnnotation._id();
	public static final _index default_index = defaultElastimorphedAnnotation._index();
	public static final _parent default_parent = defaultElastimorphedAnnotation._parent();
	public static final _routing default_routing = defaultElastimorphedAnnotation._routing();
	public static final _size default_size = defaultElastimorphedAnnotation._size();
	public static final _source default_source = defaultElastimorphedAnnotation._source();
	public static final _timestamp default_timestamp = defaultElastimorphedAnnotation._timestamp();
	public static final _ttl default_ttl = defaultElastimorphedAnnotation._ttl();
	public static final _type default_type = defaultElastimorphedAnnotation._type();

	
	private static final Logr log = MorphiaLoggerFactory.get(IndexedClass.class);
	
	
	private String typeName; //the type name used in ElasticSearch 
	
	private Map<Class<? extends Annotation>, Set<Annotation>> foundClassAnnotations = new HashMap<Class<? extends Annotation>, Set<Annotation>>();
	
	private _id _id;
	private _type _type;
	private _source _source;
	private _all _all;
	private _analyzer _analyzer;
	private _boost _boost; 
	private _parent _parent;
	private _routing _routing;
	private _index _index;
	private _size _size;
	private _timestamp _timestamp;
	private _ttl _ttl;
	
	
	//aLL FIELDS
	private LinkedHashMap<AccessibleObject, IndexedField > indexedFields = new LinkedHashMap<AccessibleObject, IndexedField>();
	
	
	//Fields that were annotated with the ElastiField annotation
	private LinkedHashMap<AccessibleObject, StandardIndexedField > standardIndexedFields = new LinkedHashMap<AccessibleObject, StandardIndexedField>();
	
	//Entity Fields that were annotated with the ElastiFieldEmbedded
	private LinkedHashMap<AccessibleObject, IndexedEmbeddedField > indexedEmbeddedFields = new LinkedHashMap<AccessibleObject, IndexedEmbeddedField>();
	
	private LinkedHashMap<AccessibleObject, IndexedMapField > indexedMapsFields = new LinkedHashMap<AccessibleObject, IndexedMapField>();
	
	private ElasticStore es;
	
	//found "@AnalyzerDefinition" annotations inside the class
//	private Map<String, AnalyzerDefinition> foudAnalyzerDefinitionAnnotations = new HashMap<String, AnalyzerDefinition>();
	
	
	
	
	private List<ElasticFilter> filterDefs = new ArrayList<ElasticFilter>();
	private List<ElasticTokenizer> tokenizerDefs = new ArrayList<ElasticTokenizer>();
	
	
	/**
	 * Analyzers that were defined in an AnalyzerDefinition annotation inside the class 
	 */
	private List<ElasticAnalyzer> analyzerDefs = new ArrayList<ElasticAnalyzer>();
	
	/**
	 * Analyzers that are defined classwide = for all fields
	 */
	private List<ElasticAnalyzer> classWideAnalyzers = new ArrayList<ElasticAnalyzer>();
	
	/**
	 * analyzers that were defined for specific fields
	 */
	private List<ElasticAnalyzer> fieldLevelAnalyzers= new ArrayList<ElasticAnalyzer>();
	
	
	
	
	//A map of analyzer definitions that should explicitely be added to the index == not references 
	private Map<String, ElasticAnalyzer> newAnalyzerDefs = new HashMap<String, ElasticAnalyzer>();
	//A map of aliazed analyzers / references
	private Map<String, ElasticAnalyzer> aliasedAnalyzers = new HashMap<String, ElasticAnalyzer>();
	
	
	boolean hasIndexedEmbeddedReferences = false;
	
	
	
	private Class<?> clazz;
	
	private Field idField; 
	
	public IndexedClass(Class<?> clazz, ElasticStore es){
		this.clazz = clazz;
		this.es = es;
		prepare();
	}
	
	
	/** Discovers interesting (that we care about) things about the class for indexation purposes. */
	protected void prepare() {
		log.debug("Morphiastic : preparing Indexed class " + clazz.getName() + " ." );
		System.out.println(">>>> Morphiastic : preparing Indexed class " + clazz.getName() + " . <<<<<<");
		
		this.filterDefs.addAll(findFilterDefs(getClazz()));
		this.tokenizerDefs.addAll(findTokenizerDefs(getClazz()));
		this.analyzerDefs.addAll(findAnalyzerDefs(getClazz()));

		//find unique class wide annotations
		for (Class<? extends Annotation> c : classAnnotationsLookup) {
			addClassWideAnnotation(c);
		}
		
		prepareGlobalProperties();
		prepareFields();
		prepareMethods();
		prepareAdditionalFieldsMapping(); //prepare additional fields that were put in an ElastiFields annotation on the class level
		
		
		
		
		//analyze the analyzers to determine which ones are actualy new analyzer definitions
		findCustomizedAnalyzers(analyzerDefs);
		findCustomizedAnalyzers(classWideAnalyzers);
		findCustomizedAnalyzers(fieldLevelAnalyzers);
		
		
		
		System.out.println(">>>>> IndexdClass prepare complete <<<<<<<");
	}
	
	
	
	/**
	 * finds  Filter definitions from annotation avaialble in the provided class
	 * and returns them as an arraylist of unique ElasticFilter
	 * @param clazz
	 * @return
	 */
	public static List<ElasticFilter> findFilterDefs(Class<?> clazz){
		List<ElasticFilter> filterDefs = new ArrayList<ElasticFilter>();
		if(clazz==null)return filterDefs;
		Map<String, FilterDefinition> foundFilterDefinitionAnnotations = new HashMap<String, FilterDefinition>();
		for ( Annotation a : clazz.getAnnotations()) {
			Class<? extends Annotation> annClass = a.annotationType();
			if(annClass.equals(FilterDefinition.class)){
				FilterDefinition filterDef = (FilterDefinition) a;
				String defName = filterDef.defName();
				if(defName.isEmpty())throw new IllegalArgumentException("FilterDefinition annotation does not provide a definition name. Class : " + clazz.getName());
				if(foundFilterDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("FilterDefinition defName used multiple times in the same class :" + defName );
				ElasticFilter filter = ElasticFilterFactory.getFilterFromAnnotation(filterDef);
				foundFilterDefinitionAnnotations.put(defName, filterDef); //only to avoid duplicates
				filterDefs.add(filter);
			}else if(annClass.equals(FilterDefinitions.class)){
				FilterDefinitions filterDefsAnnotation = (FilterDefinitions) a;
				FilterDefinition[] defs = filterDefsAnnotation.value();
				for (FilterDefinition filterDef : defs) {
					String defName = filterDef.defName();
					if(defName.isEmpty())throw new IllegalArgumentException("FilterDefinition annotation does not provide a definition name. Class : " + clazz.getName());
					if(foundFilterDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("FilterDefinition defName used multiple times in the same class :" + defName );
					ElasticFilter filter = ElasticFilterFactory.getFilterFromAnnotation(filterDef);
					foundFilterDefinitionAnnotations.put(defName, filterDef); //only to avoid duplicates
					filterDefs.add(filter);
				}
			}
		}
		return filterDefs;
	}
	
	/**
	 * finds  Tokenizer definitions from annotation avaialble in the provided class
	 * and returns them as an arraylist of unique ElasticTokenizer
	 * @param clazz
	 * @return
	 */
	public static List<ElasticTokenizer> findTokenizerDefs(Class<?> clazz){
		List<ElasticTokenizer> tokenizerDefinitions = new ArrayList<ElasticTokenizer>();
		if(clazz==null)return tokenizerDefinitions;
		Map<String, TokenizerDefinition> foundTokenizerDefinitionAnnotations = new HashMap<String, TokenizerDefinition>();
		for ( Annotation a : clazz.getAnnotations()) {
			Class<? extends Annotation> annClass = a.annotationType();
			if(annClass.equals(TokenizerDefinition.class)){
				TokenizerDefinition tokenizerDef = (TokenizerDefinition) a;
				String defName = tokenizerDef.defName();
				if(defName.isEmpty())throw new IllegalArgumentException("TokenizerDefinition annotation does not provide a definition name. Class : " + clazz.getName());
				if(foundTokenizerDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("TokenizerDefinition defName used multiple times in the same class :" + defName );
				ElasticTokenizer tokenizer = ElasticTokenizerFactory.getTokenizerFromAnnotation(tokenizerDef);
				foundTokenizerDefinitionAnnotations.put(defName, tokenizerDef); //only to avoid duplicates
				tokenizerDefinitions.add(tokenizer);
			}else if(annClass.equals(TokenizerDefinitions.class)){
				TokenizerDefinitions tokenizerDefsAnnotation = (TokenizerDefinitions) a;
				TokenizerDefinition[] defs = tokenizerDefsAnnotation.value();
				for (TokenizerDefinition tokenizerDef : defs) {
					String defName = tokenizerDef.defName();
					if(defName.isEmpty())throw new IllegalArgumentException("TokenizerDefinition annotation does not provide a definition name. Class : " + clazz.getName());
					if(foundTokenizerDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("TokenizerDefinition defName used multiple times in the same class :" + defName );
					ElasticTokenizer tokenizer = ElasticTokenizerFactory.getTokenizerFromAnnotation(tokenizerDef);
					foundTokenizerDefinitionAnnotations.put(defName, tokenizerDef); //only to avoid duplicates
					tokenizerDefinitions.add(tokenizer);
				}
			}
		}
		return tokenizerDefinitions;
	}
	
	
	/**
	 * finds class level ElasticAnalyzer definitions from annotations avaialble in the provided class
	 * and returns them as an arraylist of unique ElasticAnalyzer
	 * 
	 * @param clazz
	 * @return
	 */
	public static List<ElasticAnalyzer> findAnalyzerDefs(Class<?> clazz){
		//find AnalyzerDefinition annotations
		List<ElasticAnalyzer> analyzerDefinitions = new ArrayList<ElasticAnalyzer>();
		if(clazz==null)return analyzerDefinitions;
		Map<String, AnalyzerDefinition> foundAnalyzerDefinitionAnnotations = new HashMap<String, AnalyzerDefinition>();
		for ( Annotation a : clazz.getAnnotations()) {
			Class<? extends Annotation> annClass = a.annotationType();
			if(annClass.equals(AnalyzerDefinition.class)){
				AnalyzerDefinition anndef = (AnalyzerDefinition) a;
				String defName = anndef.defName();
				if(defName.isEmpty())throw new IllegalArgumentException("AnalyzerDefinition annotation does not provide a definition name. Class : " + clazz.getName());
				ElasticAnalyzerType type = anndef.type();
				if(type == ElasticAnalyzerType._reference || type == ElasticAnalyzerType._void)throw new IllegalArgumentException("AnalyzerDefinition type cannot be _reference OR _void. YOU MUST SELECT A PROPPER ANALYZER TYPE in " + clazz.getName());
				if(foundAnalyzerDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("AnalyzerDefinition defName used multiple times in the same class :" + defName );
				ElasticAnalyzer analyzer= ElasticAnalyzerFactory.getAnalyzerFromAnnotation(anndef);
				foundAnalyzerDefinitionAnnotations.put(defName, anndef); //only to avoid duplicates
				analyzerDefinitions.add(analyzer);
			}else if(annClass.equals(AnalyzerDefinitions.class)){
				System.out.println("Processing AnalyzerDefinitions annotation");
				AnalyzerDefinitions anndefsAnnotation = (AnalyzerDefinitions) a;
				AnalyzerDefinition[] defs = anndefsAnnotation.value();
				for (AnalyzerDefinition anndef : defs) {
					String defName = anndef.defName();
					if(defName.isEmpty())throw new IllegalArgumentException("AnalyzerDefinition annotation does not provide a definition name. Class : " + clazz.getName());
					ElasticAnalyzerType type = anndef.type();
					if(type == ElasticAnalyzerType._reference || type == ElasticAnalyzerType._void)throw new IllegalArgumentException("AnalyzerDefinition type cannot be _reference OR _void. YOU MUST SELECT A PROPPER ANALYZER TYPE in " + clazz.getName());
					if(foundAnalyzerDefinitionAnnotations.get(defName)!=null) throw new IllegalArgumentException("AnalyzerDefinition defName used multiple times in the same class :" + defName );
					ElasticAnalyzer analyzer= ElasticAnalyzerFactory.getAnalyzerFromAnnotation(anndef);
					foundAnalyzerDefinitionAnnotations.put(defName, anndef); //only to avoid duplicates
					analyzerDefinitions.add(analyzer);
				}
			}
		}
		return analyzerDefinitions;
	}
	
	private void prepareGlobalProperties(){
		Set<Annotation> annos =  foundClassAnnotations.get(ElastiMorphed.class);
		if(annos!=null && annos.size() == 1){
			Annotation annotation = annos.iterator().next();
			ElastiMorphed elastiMorphedAnno =  (ElastiMorphed) annotation;
			determineTypeName(elastiMorphedAnno);
		
			this._id = elastiMorphedAnno._id();
			this._type = elastiMorphedAnno._type();
			this._source = elastiMorphedAnno._source();
			this._all = elastiMorphedAnno._all();
			this._analyzer = elastiMorphedAnno._analyzer();
			this._boost = elastiMorphedAnno._boost();
			this._parent = elastiMorphedAnno._parent();
			this._routing = elastiMorphedAnno._routing();
			this._index = elastiMorphedAnno._index();
			this._size = elastiMorphedAnno._size();
			this._timestamp = elastiMorphedAnno._timestamp();
			this._ttl = elastiMorphedAnno._ttl();
			
		}
	}

	private void determineTypeName(ElastiMorphed elastiMorphedAnno){
		String typeName = elastiMorphedAnno.typeName();
		if (typeName.isEmpty()) {
			typeName = getDefaultIndexNameForClass(this.clazz);
		}
		this.typeName = typeName;
	}
	
	
	
	private void prepareFields(){
		//prepare fields
		List<Field> idAnnotations = new ArrayList<Field>();
		for (Field field : ReflectionUtils.getDeclaredAndInheritedFields(clazz, true)) {
			field.setAccessible(true);
			if (field.isAnnotationPresent(Id.class)){
				idAnnotations.add(field);
			}
			if (field.isAnnotationPresent(ElastiField.class)){
				addStandardIndexedField(field);
			}
			if(field.isAnnotationPresent(ElastiFieldEmbedded.class)){
				addEmbeddedField(field);
			}
			if(field.isAnnotationPresent(ElastiMap.class)){
//				System.out.println("Elastimap annotation found ");
				addMapField(field);
			}
			
		}
		if(idAnnotations.isEmpty()){
			throw new IllegalArgumentException("Class"+ clazz.getName() +" was not properly annotated with an @Id field");
		}else if(idAnnotations.size()>1){
			throw new IllegalArgumentException("Multiple @ID fields found in class "+ clazz.getName());
		}else{
			this.idField = idAnnotations.get(0);
		}
		
	}
	

	
	private void prepareMethods(){
	
		for(Method method : ReflectionUtils.getDeclaredAndInheritedMethods(clazz)){
			method.setAccessible(true);
			
				if (method.isAnnotationPresent(ElastiField.class)){
					if( method.getReturnType().equals(Void.TYPE)){
						System.err.println("the metod named " + method.getName() + "in class " + clazz.getName() + " is void  ... nothing will be indexed from it .. the mappping def has not been added");
					}else{
						addStandardIndexedMethod(method);
					}
				}
				if(method.isAnnotationPresent(ElastiFieldEmbedded.class)){
					if( method.getReturnType().equals(Void.TYPE)){
						System.err.println("the metod named " + method.getName() + "in class " + clazz.getName() + " is void  ... nothing will be indexed from it .. the mappping def has not been added");
					}else{
						addEmbeddedMethod(method);
					}
				}
				if(method.isAnnotationPresent(ElastiMap.class)){
//					System.out.println("Elastimap annotation found ");
					if( method.getReturnType().equals(Void.TYPE)){
						System.err.println("the metod named " + method.getName() + "in class " + clazz.getName() + " is void  ... nothing will be indexed from it .. the mappping def has not been added");
					}else{
						addMapMethod(method);
					}
				}
		}
	}
	
	
	
	private void prepareAdditionalFieldsMapping(){
		
		
		
		
	}

	private void addStandardIndexedMethod(Method method){
		ElastiField fan = method.getAnnotation(ElastiField.class);
		if(fan!=null){
			StandardIndexedField indexationRule = new StandardIndexedField(method, es);
			this.standardIndexedFields.put(method, indexationRule);
			this.indexedFields.put(method, indexationRule);
			ElasticAnalyzer indexAn = indexationRule.getAnalyzer();
			ElasticAnalyzer searchAn = indexationRule.getSearchAnalyzer();
			this.fieldLevelAnalyzers.add(indexAn);
			if(searchAn!=null)this.fieldLevelAnalyzers.add(searchAn);
		}
	}
	
	
	private void addEmbeddedMethod(Method method){
		ElastiFieldEmbedded fan = method.getAnnotation(ElastiFieldEmbedded.class);
		if(fan!=null){
			IndexedEmbeddedField indexationRule = null;
			if(method.getReturnType().isArray()){
				indexationRule = new IndexedEmbeddedField(method.getReturnType().getComponentType(), method.getName(), fan, null, null, true ,es);
			}else if(Collection.class.isAssignableFrom(method.getReturnType())){
				Type genericType = method.getGenericReturnType();
				 if (genericType instanceof ParameterizedType) {  
			            ParameterizedType pt = (ParameterizedType) genericType;  
			            Class<?>  c = (Class<?>) pt.getActualTypeArguments()[0];
			            indexationRule = new IndexedEmbeddedField(c, method.getName(), fan, null, null, true,es);
			      }  
			}else{
				indexationRule = new IndexedEmbeddedField(method,es);
			}
			indexedEmbeddedFields.put(method, indexationRule);
			this.indexedFields.put(method, indexationRule);
			Set<ElasticAnalyzer> embeddedAnalyzers = indexationRule.getAnalyzers();
			this.fieldLevelAnalyzers.addAll(embeddedAnalyzers);
		}
	}
	
	
	private void addMapMethod(Method method){
		ElastiMap fan = method.getAnnotation(ElastiMap.class);
		if(fan!=null){
			IndexedMapField indexationRule = new IndexedMapField(method,es);
			this.indexedMapsFields.put(method, indexationRule);
			this.indexedFields.put(method, indexationRule);
			this.fieldLevelAnalyzers.addAll(indexationRule.getAnalyzers());
		}
	}
	
	
	
	/**
	 * Checks the annotations on a specific field to see how it should be analzed and indexed
	 * @param field
	 */
	private void addStandardIndexedField(Field field){
		//get each annotation value and create an IndexationRule
		ElastiField fan = field.getAnnotation(ElastiField.class);
		if(fan!=null){
			StandardIndexedField indexationRule = new StandardIndexedField(field,es);
			this.standardIndexedFields.put(field, indexationRule);
			this.indexedFields.put(field, indexationRule);
			ElasticAnalyzer indexAn = indexationRule.getAnalyzer();
			ElasticAnalyzer searchAn = indexationRule.getSearchAnalyzer();
			this.fieldLevelAnalyzers.add(indexAn);
			if(searchAn!=null)this.fieldLevelAnalyzers.add(searchAn);
		}
	}

	private void addEmbeddedField(Field field){
		ElastiFieldEmbedded fan = field.getAnnotation(ElastiFieldEmbedded.class);
		if(fan!=null){
			IndexedEmbeddedField indexationRule = null;
			if(field.getType().isArray()){
				indexationRule = new IndexedEmbeddedField(field.getType().getComponentType(), field.getName(), fan, null, null, false,es);
			}else if(Collection.class.isAssignableFrom(field.getType())){
				Type genericType = field.getGenericType();
				 if (genericType instanceof ParameterizedType) {  
			            ParameterizedType pt = (ParameterizedType) genericType;  
			            Class<?>  c = (Class<?>) pt.getActualTypeArguments()[0];
			            indexationRule = new IndexedEmbeddedField(c, field.getName(), fan, null, null, false,es);
			      }  
			}else{
				indexationRule = new IndexedEmbeddedField(field,es);
			}
			indexedEmbeddedFields.put(field, indexationRule);
			this.indexedFields.put(field, indexationRule);
			Set<ElasticAnalyzer> embeddedAnalyzers = indexationRule.getAnalyzers();
			this.fieldLevelAnalyzers.addAll(embeddedAnalyzers);
		}
	}
	
	
	private void addMapField(Field field){
		ElastiMap fan = field.getAnnotation(ElastiMap.class);
		
		if(fan!=null){
			IndexedMapField indexationRule = new IndexedMapField(field,es);
			this.indexedMapsFields.put(field, indexationRule);
			this.indexedFields.put(field, indexationRule);
			this.fieldLevelAnalyzers.addAll(indexationRule.getAnalyzers());
		}
	}
	
	
	
	
	
	/**
	 * Adds Filter definitions, tokenizer definitions, and analyzer definitions to the 
	 * ElasticStore which in turn sends the definitions to the ElasticSearch server
	 * @param es
	 */
	protected void addAnalysisComponentsToStore(ElasticStore es){
		System.err.println("       >> "+ getClazz() +" addAnalysisComponentsToStore ...");
		for (ElasticFilter filterDef : filterDefs) {
			es.sendComponentDefinitionToServer(filterDef);
		}
		addEmbeddedFilterDefsToStore(  es);
		
		for (ElasticTokenizer tokenizerDef : tokenizerDefs) {
			es.sendComponentDefinitionToServer(tokenizerDef);
		}
		addEmbeddedTokenizerDefsToStore(es);
		
		
		for (ElasticAnalyzer customizedAnalyzer : this.newAnalyzerDefs.values()) {
			es.sendComponentDefinitionToServer(customizedAnalyzer);
		}
		
		System.err.println("addAnalysisComponentsToStore adding aliases");
		for (ElasticAnalyzer analAlias : this.aliasedAnalyzers.values()) {
			es.addAnalyzerAlias(analAlias.getElasticAnalyzerType().toString(), analAlias.getDefName(), analAlias.getElasticAnalyzerType());
		}
		
		System.err.println("       >> FINISHED "+ getClazz() +" addAnalysisComponentsToStore ...");
	}
	
	
	private void addEmbeddedFilterDefsToStore(ElasticStore es){
		System.out.println(" IndexedClass "+getClazz().getName() + " addEmbeddedFilterDefsToStore ");
		for (IndexedEmbeddedField embeddedField : indexedEmbeddedFields.values()) {
			Set<ElasticFilter> embededFilters = embeddedField.getFilterDefinitions();
			for (ElasticFilter embeddedFilterDef : embededFilters) {
				es.sendComponentDefinitionToServer(embeddedFilterDef);
			}
		}
	}
	
	
	private void addEmbeddedTokenizerDefsToStore(ElasticStore es){
		for (IndexedEmbeddedField embeddedField : indexedEmbeddedFields.values()) {
			Set<ElasticTokenizer> embeddedTokenizerDefs = embeddedField.getTokenizerDefinitions();
			for (ElasticTokenizer embeddedTokenizerDef : embeddedTokenizerDefs) {
				es.sendComponentDefinitionToServer(embeddedTokenizerDef);
			}
		}
	}
	
	

	
	
	
	
	
	
	/**
	 * Adds the annotation, if it exists  
	 * @param clazz
	 */
	private void addClassWideAnnotation(Class<? extends Annotation> c) {
		Annotation ann = ReflectionUtils.getAnnotation(getClazz(), c);
		if (ann != null){
			Set<Annotation> classAnnList = foundClassAnnotations.get(c);
			if(classAnnList == null)classAnnList = new HashSet<Annotation>();
			classAnnList.add(ann);
 			foundClassAnnotations.put(c, classAnnList);
		}
	}



	
	
	/**
	 * Method used to actualy create the index structure on the Elastic search server
	 * @param elasticStore
	 */
	public void createIndexMapping(ElasticStore elasticStore){
		System.out.println("INDEXEDCLASS createIndexMapping fired on " + this.typeName + " clazz name = " + this.clazz.getName());
//		caca
		
		
		
//		HashMap<String, Object> mappingProps = getMappingAsMap();
//		if(elasticStore.mappingExists(this.typeName, mappingJSON))return;
		
		
		XContentBuilder mappingBuilder;
		try {
			
			
			mappingBuilder = getMappingBuilder();
			
			JsonNode mappingJSON = getMappingAsJSONNode(mappingBuilder);
			
			if(elasticStore.mappingExists(this.typeName, mappingJSON))return;
			
			//otherwise put that friggin mapping
			elasticStore.putMapping(this.typeName, mappingBuilder);  // , mappingProps
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
//		System.out.println("INDEX MAPPING FOR CLASS " + this.clazz.getName() + " AKA " + this.typeName +" was created ");
	}
	
	
	
	
	/**
	 * Builds the JSON representation of this indexed class
	 * @return
	 * @throws IOException
	 */
	private XContentBuilder getMappingBuilder() throws IOException{
		
			XContentBuilder mappingBuilder =  XContentFactory.jsonBuilder()
				.startObject()
				.startObject(this.typeName)
				.field("dynamic", ""+ false); //Added 09.06.12
			
			add_DOCUMENTID_FieldMapping(mappingBuilder); 
//				.field("dynamic", false)   //TODO add this as a setting available in ELastiMorphed annotation
			mappingBuilder.startObject("properties");
	 
			//TODO add type mapping rules of the ElastiMorphed annotation  see public @interface ElastiMorphed annotation file
			//TODO if there are entity/class wide rules to add add them here ! 
			
			
			Set<AccessibleObject> fields = this.standardIndexedFields.keySet();
			for (AccessibleObject field : fields) {
				StandardIndexedField rule = this.standardIndexedFields.get(field);
				rule.addIndexFieldSettings(mappingBuilder);
			}
			Set<AccessibleObject> embeddedFields = this.indexedEmbeddedFields.keySet();
			for(AccessibleObject field : embeddedFields){
				IndexedEmbeddedField rule = this.indexedEmbeddedFields.get(field);
				rule.addIndexFieldSettings(mappingBuilder);
			}
			
			Set<AccessibleObject> mapFields = this.indexedMapsFields.keySet();
			for(AccessibleObject field : mapFields){
				IndexedMapField rule = this.indexedMapsFields.get(field);
				rule.addIndexFieldSettings(mappingBuilder);
			}
			
			addMongoIdField(mappingBuilder);
			
			
		return mappingBuilder;
	}
	
	
	
	public String getMappingAsJSONString() throws IOException{
		return getMappingBuilder().string();
	}
	
	
	public JsonNode getMappingAsJSONNode() throws JsonParseException, IOException {
		
		JsonParser jp = ElasticStore.factory.createJsonParser(getMappingAsJSONString());
		JsonNode actualObj = ElasticStore.mapper.readTree(jp);
		return actualObj;
		
	}
	
	
	
	public JsonNode getMappingAsJSONNode(XContentBuilder mappingBuilder) throws JsonParseException, IOException {
		
		JsonParser jp = ElasticStore.factory.createJsonParser(mappingBuilder.string());
		JsonNode jsonNode = ElasticStore.mapper.readTree(jp);
		return jsonNode;
		
	}
	
	
	
//	caca now we want to use a real JSON object foo
	/**
	 * Builds the mapping of this class as a MAP
	 * @return
	 */
	@Deprecated
	private HashMap<String, Object> getMappingAsMap(){
		
		HashMap<String, Object> m = new HashMap<String, Object>();
		m.put("dynamic", ""+false);
		if(!this._all.equals(default_all)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._all.enabled()!=default_all.enabled())props.put(ENABLED_PROPKEY, ""+this._all.enabled());
			if(!this._all.store().equals(default_all.store()))props.put(STORE_KEYPROP, this._all.store().toString());
			if(!this._all.termVector().equals(default_all.termVector()))props.put(TERMVECTOR_PROPKEY, this._all.termVector().toString());
			if(!this._all.index_analyzer().equals(default_all.index_analyzer()))props.put(ANALYZER_PROPKEY, this._all.index_analyzer().toString());
			if(!this._all.search_analyzer().equals(default_all.search_analyzer()))props.put(SEARCH_ANALYZER_PROPKEY, this._all.search_analyzer().toString());
			m.put("_all", props);
		}
		
		if(!this._analyzer.equals(default_analyzer)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._analyzer.index().equals(default_analyzer.index()))props.put(INDEX_PROPKEY, this._analyzer.index().toString());
			if(!this._analyzer.path().equals(default_analyzer.path()))props.put(PATH_PROPKEY, this._analyzer.path().toString());
			m.put("_analyzer", props);
		
		}
		
		if(!this._boost.equals(default_boost)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._boost.name().equals(default_boost.name()))props.put(NAME_PROPKEY, this._boost.name().toString());
			if(this._boost.null_value()!=default_boost.null_value())props.put(NULL_VALUE_PROPKEY, ""+this._boost.null_value());
			m.put("_boost", props);
		}
		
		if(!this._id.equals(default_id)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._id.index().equals(default_id.index()))props.put(INDEX_PROPKEY, this._id.index().toString());
			if(!this._id.store().equals(default_id.store()))props.put(STORE_PROPKEY, this._id.store().toString());
			if(!this._id.path().equals(default_id.path()))props.put(PATH_PROPKEY, this._id.path().toString());
			m.put("_id", props);
		}else{
			//default _id mapping is path = _mid 
			//Added 09.06.12
			HashMap<String, Object> props = new HashMap<String, Object>();
			props.put("path", MID_FIELD_KEY);
			m.put(ELASTICSEARCH_DOCUMENT_ID_FIELD_KEY, props);
 
		}
		
		if(!this._index.equals(default_index)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._index.value() != default_index.value())props.put(ENABLED_PROPKEY , ""+this._index.value());
			m.put("_index", props);
		}
		
		if(!this._parent.equals(default_parent)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._parent.value().equals(default_parent.value()))props.put(TYPE_PROPKEY , this._parent.value().toString());
			m.put("_parent", props);
		}
		
		if(!this._routing.equals(default_routing)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._routing.store().equals(default_routing.store()))props.put(STORE_PROPKEY , this._routing.store().toString());
			if(!this._routing.index().equals(default_routing.index()))props.put(INDEX_PROPKEY, this._routing.index().toString());
			if(this._routing.required()!=default_routing.required())props.put(REQUIRED_PROPKEY, ""+this._routing.required());
			if(!this._routing.path().equals(default_routing.path()))props.put(PATH_PROPKEY , this._routing.path().toString());
			m.put("_routing", props);
		}
		
		if(!this._size.equals(default_size)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._size.enabled()!=default_size.enabled())props.put(ENABLED_PROPKEY, ""+this._size.enabled());
			if(!this._size.store().equals(default_size.store()))props.put(STORE_PROPKEY , this._size.store().toString());
			m.put("_size", props);
		}
		
		if(!this._source.equals(default_source)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._source.enabled()!=default_source.enabled())props.put(ENABLED_PROPKEY, ""+this._source.enabled());
			if(this._source.compress()!=default_source.compress())props.put(COMPRESS_PROPKEY, ""+this._source.compress());
			if(!this._source.compress_threshold().equals(default_source.compress_threshold()))props.put(COMPRESS_THRESHOLD_PROPKEY , this._source.compress_threshold().toString());
			if(!this._source.includes().equals(default_source.includes()))props.put(INCLUDES_PROPKEY , this._source.includes());
			if(!this._source.excludes().equals(default_source.excludes()))props.put(EXCLUDES_PROPKEY , this._source.excludes());
			m.put("_source", props);
		}
		
		if(!this._timestamp.equals(default_timestamp)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._timestamp.enabled()!=default_timestamp.enabled())props.put(ENABLED_PROPKEY, ""+this._timestamp.enabled());
			if(!this._timestamp.store().equals(default_timestamp.store()))props.put(STORE_PROPKEY , this._timestamp.store().toString());
			if(!this._timestamp.index().equals(default_timestamp.index()))props.put(INDEX_PROPKEY, this._timestamp.index().toString());
			if(!this._timestamp.path().equals(default_timestamp.path()))props.put(PATH_PROPKEY , this._timestamp.path().toString());
			if(!this._timestamp.format().equals(default_timestamp.format()))props.put(FORMAT_PROPKEY , this._timestamp.format().toString());
			m.put("_timestamp", props);
		}
		
		if(!this._ttl.equals(default_ttl)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(this._ttl.enabled()!=default_ttl.enabled())props.put(ENABLED_PROPKEY, ""+this._ttl.enabled());
			if(!this._ttl.store().equals(default_ttl.store()))props.put(STORE_PROPKEY , this._ttl.store().toString());
			if(!this._ttl.index().equals(default_ttl.index()))props.put(INDEX_PROPKEY, this._ttl.index().toString());
			if(!this._ttl.defaultt().equals(default_ttl.defaultt()))props.put(DEFAULT_PROPKEY , this._ttl.defaultt().toString());
			m.put("_ttl", props);
		}
		
		if(!this._type.equals(default_type)){
			HashMap<String, Object> props = new HashMap<String, Object>();
			if(!this._type.store().equals(default_type.store()))props.put(STORE_PROPKEY , this._type.store().toString());
			if(!this._type.index().equals(default_type.index()))props.put(INDEX_PROPKEY, this._type.index().toString());
			m.put("_type", props);
		}
		
		
		
		
		if(!this.standardIndexedFields.isEmpty() || !this.indexedEmbeddedFields.isEmpty() || !this.indexedMapsFields.isEmpty()){
			//we have some field props to add
			
			
			HashMap<String, Object> props = new HashMap<String, Object>();
		
			
			for (StandardIndexedField indexedField : this.standardIndexedFields.values()) {	
				indexedField.addFieldSettingsMap(props);
			}
			
			for (IndexedEmbeddedField indexedField : this.indexedEmbeddedFields.values()) {	
				indexedField.addFieldSettingsMap(props);
			}
			
			for (IndexedMapField indexedField : this.indexedMapsFields.values()) {	
				indexedField.addFieldSettingsMap(props);
			}
			
			HashMap<String, Object> midFieldProps = new HashMap<String, Object>();
			midFieldProps.put("type", StandardIndexedField.getElasticTypeNameOfField(this.idField));
			midFieldProps.put("store", Store.yes.toString());
			midFieldProps.put("index", Analyze.not_analyzed.toString());		
//			if(this.idField.getType().equals(ObjectId.class)){
//				System.err.println("IDFIELD IS AN OBJECTID");
//				HashMap<String, Object> idObjProps = new HashMap<String, Object>();
//				HashMap<String, Object> oidProps = new HashMap<String, Object>();
//				oidProps.put("type", "string");
//				oidProps.put("index", Analyze.not_analyzed.toString());
//				oidProps.put("store", Store.yes.toString());
//				idObjProps.put("$oid", oidProps);
////				String idProps = "{\"$oid\" : {\"type\" : \"string\", \"index\" : \"not_analyzed\"}";
//				idFieldProps.put("properties",idObjProps);
//			}
			
			
			
			props.put(MID_FIELD_KEY,midFieldProps);//_mid  ... should it be here ? 
			
			//TODO add embedded fields here aswell
			m.put("properties", props);
		}
		
		return m;
	}
	
	
	/**
	 * 
	 * TEMPORARY CODE ... we should use Dcoument type wide ElastiMorphed annotation rules here instead
	 * see public @interface ElastiMorphed annotation file for all the fields available
	 * 
	 * 
	 * morphiastic uses the _mid field as ES document _id field by default
	 * 
	 * 
	 * @param mappingBuilder
	 * @throws IOException
	 */
	private void add_DOCUMENTID_FieldMapping(XContentBuilder mappingBuilder) throws IOException{
		mappingBuilder.startObject(ELASTICSEARCH_DOCUMENT_ID_FIELD_KEY);
		mappingBuilder.field("path", MID_FIELD_KEY);
		mappingBuilder.endObject();
	}
	
	
	
	private void addMongoIdField(XContentBuilder mappingBuilder) throws IOException{
		mappingBuilder.startObject(MID_FIELD_KEY); //
		mappingBuilder.field("type", StandardIndexedField.getElasticTypeNameOfField(this.idField));
		mappingBuilder.field("store", Store.yes);
		mappingBuilder.field("index", Analyze.not_analyzed);		
		mappingBuilder.endObject();
	}
	
	private void findCustomizedAnalyzers(List<ElasticAnalyzer> analyzers){
		for (ElasticAnalyzer analyzer : analyzers) {
			ElasticAnalyzerType analyzerType = analyzer.getElasticAnalyzerType();
			if(analyzerType != ElasticAnalyzerType._reference &&
			   analyzerType!=ElasticAnalyzerType._void	){
				//figure out if we have cutom params or we are just dealing with one of the default analyzers with default settings
				if(analyzer.hasCustomParams()){
//					System.out.println(">> CUSTOMIZED ANALYZER FOUND");
//					System.out.println(analyzer.getDefName() + " tokenizer = " + analyzer.getDefinitionName());
					this.newAnalyzerDefs.put(analyzer.getDefName(), analyzer);
					
				}else if(!analyzer.getDefName().isEmpty()){
					if(! analyzer.getDefName().equals(analyzer.getElasticAnalyzerType().toString())){
						System.out.println("ALIAS ANALYZER FOUND " + analyzer.getDefName());
						// we are trying to alias an existing analyzer
						this.aliasedAnalyzers.put(analyzer.getDefName(), analyzer);
					}
				}
			}
		}
	}
	
	
	
	
	
	public XContentBuilder entityToIndexableObject(XContentBuilder x, Object o) throws IOException{
//		System.out.println("entityToIndexableObject ID FILED = " + this.idField);
		boolean idWasDone = false;
		
			x.startObject();
			try{
				for (Map.Entry<AccessibleObject, IndexedField> indexedField : indexedFields.entrySet()) { 
					AccessibleObject field = indexedField.getKey();
					
					if(field instanceof Field){
//						System.err.println("entityToIndexableObject performing field : " + field);	
						if(field == this.idField){  //.isAnnotationPresent(Id.class)
							Object fieldValue = ((Field) field).get(o);
							x.field("_mid", fieldValue); //TODO check if conversion is needed here
							idWasDone = true;
						}else{
//							Object fieldValue = field.get(o);
							indexedField.getValue().writeIndexedField(x, o, es, null, new IdentityHashMap<AccessibleObject, Integer>());
						}
					}else if(field instanceof Method){
						
						indexedField.getValue().writeIndexedField(x, o, es, null, new IdentityHashMap<AccessibleObject, Integer>());
						
					}
				}
				if(!idWasDone){
					Object fieldValue = this.idField.get(o);
					x.field(MID_FIELD_KEY, fieldValue.toString());
					idWasDone = true;
				}
			}catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		x.endObject();
		
//		System.out.println("idwasdonedood ???  " + idWasDone);
		return x;
		
	}
	
	
	public String getEntityDocumentID(Object entity){
		 try {
			 this.idField.setAccessible(true);
			 Object fieldValue = this.idField.get(entity);
			 return fieldValue.toString();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new UnsupportedOperationException("Error trying to access ID field ... ");
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new UnsupportedOperationException("Error trying to access ID field ... ");
		}
	}
	
	
	@Deprecated
	@SuppressWarnings("unchecked")
	public void indexObject(String globalIndexName, Object entity, DBObject dbObj,
			Client client) throws IllegalArgumentException, IllegalAccessException {
//		Object idVal = idField.get(entity);
		
		DBObject clone = new BasicDBObject() ;
		clone.putAll(dbObj);
		Object idobj = clone.get("_id");
		clone.removeField("_id");
		clone.put("_mid",idobj );
		
		client.prepareIndex(globalIndexName, this.typeName) //, idVal.toString() 
         .setSource(clone.toMap())
         .execute().actionGet();
	}
	
	public XContentBuilder indexObject(String globalIndexName, Object entity, Client client) throws IOException{
		XContentBuilder x = entityToIndexableObject(jsonBuilder(), entity);
		
		System.out.println("ABOUT TO INDEX ThE FOLLOWING : \n " + x.string());

		client.prepareIndex(globalIndexName, this.typeName) //, idVal.toString() 
        .setSource(x)
        .execute().actionGet();
		
		return x;
	}
	
	
	

	/**
	 * should return the name that should be used as the index name for the given class
	 * this is used when the indexName parameter has not been set inside the ElastiMorphed annotation
	 * 
	 * normally we should try and figure out the topmost ancestor class of the hierarchy
	 * and use its name
	 * 
	 * @return
	 */
	public String getDefaultIndexNameForClass( Class<?> clazz ){
		//TEMPORARY TODO do it properly
		return clazz.getName();
	}
	
	
	
	
	
	/**
	 * @return the clazz
	 */
	public Class<?> getClazz() {
		return clazz;
	}



	/**
	 * @param clazz the clazz to set
	 */
	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}


	/**
	 * @return the typeName
	 */
	public String getTypeName() {
		return typeName;
	}


	/**
	 * @param typeName the typeName to set
	 */
	public void setTypeName(String typeName) {
		this.typeName = typeName;
	}


	


	/**
	 * @return the idField
	 */
	public Field getIdField() {
		return idField;
	}


	/**
	 * @param idField the idField to set
	 */
	public void setIdField(Field idField) {
		this.idField = idField;
	}
	
	
	 
//	private static class _id{
//		Index index;
//		Store store;
//		String path;
//		
//		static _id fromAnnotation(com.morphiastic.annotations._id anno){
//			_id _id = new _id();
//			_id.index = anno.index();
//			
//		}
//		
//	}
//	
//	private static class _type{
//		String store;
//		String index;
//		
//static _type fromAnnotation(com.morphiastic.annotations._type anno){
//			
//		}
//	}
//	
//	private static class _source{
//		boolean enabled = true;
//		boolean compress = false;
//		String compress_threshold;
//		Set<String> includes;
//		Set<String> excludes;
//	
//static _source fromAnnotation(com.morphiastic.annotations._source anno){
//			
//		}
//	}
//	
//	private static class _all{
//		boolean enabled = true;
//		String store;
//		String term_vector;
//		String index_analyzer;
//		String search_analyzer;
//static _all fromAnnotation(com.morphiastic.annotations._all anno){
//			
//		}
//	}
//	
//	
//	private static class _analyzer{
//		String path;
//		String index; //YES / NO
//static _analyzer fromAnnotation(com.morphiastic.annotations._analyzer anno){
//			
//		}
//	}
//	
//	private static class _boost{
//		String name;
//		float null_value; 
//static _boost fromAnnotation(com.morphiastic.annotations._boost anno){
//			
//		}
//	}
//	
//	private  static class _parent{
//		String type;
//static _parent fromAnnotation(com.morphiastic.annotations._parent anno){
//			
//		}
//	}
//	
//	private static class _routing{
//		String store;
//		String index;
//		boolean required;
//		String path;
//static _routing fromAnnotation(com.morphiastic.annotations._routing anno){
//			
//		}
//	}
//	
//	private static class _index{
//		boolean enabled;
//static _index fromAnnotation(com.morphiastic.annotations._index anno){
//			
//		}
//	}
//	
//	private static class _size{
//		boolean enabled;
//		String store;
//static _size fromAnnotation(com.morphiastic.annotations._size anno){
//			
//		}	
//	}
//	
//	private static class _timestamp{
//		boolean enabled;
//		String store;
//		String index;
//		String path;
//		String dateFormat;
//static _timestamp fromAnnotation(com.morphiastic.annotations._timestamp anno){
//			
//		}
//	}
//	
//	
//	private static class _ttl{
//		boolean enabled;
//		String store;
//		String index;
//		String deflt;
//static _ttl fromAnnotation(com.morphiastic.annotations._ttl anno){
//			
//		}
//	}
	
	@ElastiMorphed
	static final class DefaultElastiMorphedValueAccessor{
		static ElastiMorphed get(){
			return DefaultElastiMorphedValueAccessor.class.getAnnotation ( ElastiMorphed.class ) ;
		}
	}
	
}
